/*
 * IteradorIndice.cpp
 *
 *  Created on: 23/10/2011
 *      Author: diego
 */

#include "IteradorIndice.h"

IteradorIndice::IteradorIndice(string prefijo) {
	Configuracion* conf = Configuracion::getConf(PATH_CONF);
	string path_indice = conf->path_indice() + prefijo + "_indice.dat";
	this->indice = new ifstream(path_indice.c_str(), ios::in | ios::binary);

	string path_docs = conf->path_indice() + prefijo + "_docs.dat";
	this->docs = new BitReader(path_docs.c_str());

	string path_terminos = conf->path_indice() + prefijo + "_terminos.dat";
	this->terminos = new ifstream(path_terminos.c_str(), ios::in);


	this->paso = 1;

	string path_header = conf->path_indice() + prefijo + "_header.dat";
	this->header = new ifstream(path_header.c_str(), ios::in | ios::binary);

	reg_header_indice header;
	// leo el encabezado donde me dice la cantidad de registros del archivo
	this->header->read(reinterpret_cast<char *>(&header), sizeof(reg_header_indice));
	this->cant_registros = header.cant_registros;
	this->header->close();
	this->pos_actual = 0;


	this->termino_actual = NULL;
	this->termino_anterior = NULL;

}

IteradorIndice::IteradorIndice() {
	Configuracion* conf = Configuracion::getConf(PATH_CONF);
	string path_indice = conf->path_indice() + "indice.dat";
	this->indice = new ifstream(path_indice.c_str(), ios::in | ios::binary);

	string path_docs = conf->path_indice() + "docs.dat";
	this->docs = new BitReader(path_docs.c_str());

	string path_terminos = conf->path_indice() + "terminos.dat";
	this->terminos = new ifstream(path_terminos.c_str(), ios::in);

	this->paso = 1;

	string path_header = conf->path_indice() + "header.dat";
	this->header = new ifstream(path_header.c_str(), ios::in | ios::binary);

	reg_header_indice header;
	// leo el encabezado donde me dice la cantidad de registros del archivo
	this->header->read(reinterpret_cast<char *>(&header), sizeof(reg_header_indice));
	this->cant_registros = header.cant_registros;
	this->header->close();
	this->pos_actual = 0;

	this->termino_actual = NULL;
	this->termino_anterior = NULL;
}

int IteradorIndice::getCantTerminos(){
	return this->cant_registros;
}

bool IteradorIndice::tieneSiguiente(){
	return (this->cant_registros > this->pos_actual);
}
super_reg* IteradorIndice::proximoRegistro(){

	this->pos_actual++;

	super_reg* registro = new super_reg;
	reg_indice_fijo reg_indice, reg_indice_void;
	//reg_docs_variable reg_docs;
	unsigned int i;

	char *buffer;

	this->indice->read(reinterpret_cast<char *>(&reg_indice), sizeof(reg_indice_fijo));

	registro->frec_t = reg_indice.frec_t;
	registro->cant_docs = reg_indice.cant_docs;
//cout << "distintos: " << reg_indice.distintos << endl;
	buffer = new char[reg_indice.distintos];

	if (this->terminos->tellg() != reg_indice.offset_terminos){
		this->terminos->seekg(reg_indice.offset_terminos, ios::beg);
	}

	this->terminos->read(buffer, reg_indice.distintos);

	this->reconstruirTermino(reg_indice, buffer);

	delete buffer;

	registro->termino = string(this->termino_actual);

	//leo y reconstruyo los terminos siguientes para dar el paso
	for (i = 0; (i < this->paso - 1) && /*(!this->indice->eof())*/this->tieneSiguiente(); i++){
		this->pos_actual++;
		this->indice->read(reinterpret_cast<char *>(&reg_indice_void), sizeof(reg_indice_fijo));

		buffer = new char[reg_indice_void.distintos];
		this->terminos->read(buffer, reg_indice_void.distintos);

		this->reconstruirTermino(reg_indice_void, buffer);

		delete buffer;
	}

	this->docs->posicionar(reg_indice.offset_docs);
	registro->lista = new Lista<reg_docs_variable*>();
	int doc_id = 0;

	reg_docs_variable* reg_docs;

	for (i = 0; i < reg_indice.cant_docs; i++){
		doc_id += deltaDecode(this->docs);
		reg_docs = new reg_docs_variable;
		reg_docs->doc_id = doc_id;
		reg_docs->frec = deltaDecode(this->docs);
		registro->lista->agregar(reg_docs);
	}

	return registro;
}

void IteradorIndice::reconstruirTermino(reg_indice_fijo reg, char* buffer){
	unsigned int i;
	if (this->termino_anterior != NULL){
		delete this->termino_anterior;
	}

	this->termino_anterior = this->termino_actual;
	this->termino_actual = new char[reg.repetidos + reg.distintos];

	if (this->termino_anterior != NULL){
		for (i = 0; i < reg.repetidos; i++){
			this->termino_actual[i] = this->termino_anterior[i];
		}
	}

	for (i = 0; i < reg.distintos; i++){
		this->termino_actual[reg.repetidos + i] = buffer[i];
	}

	if (this->termino_anterior != NULL){
		delete this->termino_anterior;
		this->termino_anterior = NULL;
	}
}


void IteradorIndice::setPaso(int p){
	if (p > 0){
		this->paso = p;
	}
}

int IteradorIndice::getPosicion(){
	return this->pos_actual;
}

void IteradorIndice::setPosicion(int pos){
	this->indice->seekg(pos * sizeof(reg_indice_fijo), ios::beg);
}

super_reg* IteradorIndice::buscar(string termino, int pos_inicial, int tam_pagina){
	Configuracion* conf = Configuracion::getConf(PATH_CONF);
	int pos;

	if (tam_pagina > 1){
		//busqueda binaria
		pos = this->busquedaBinaria(termino, pos_inicial, tam_pagina, conf->front_coding());
	}else{
		pos = pos_inicial;
	}

	// busqueda secuencial sobre el bloque que tiene que estar el termino
	//Me ubico en el Bloque de front coding y lo itero hasta encontrar la palabra
	IteradorIndice itInd;
	itInd.setPaso(1);
	itInd.setPosicion(pos);

	super_reg* reg = NULL;
	for (int i = 0; i < conf->front_coding(); i++) {
		if (itInd.tieneSiguiente()) {
			reg = itInd.proximoRegistro();
			if (reg->termino == termino) {
				return reg;
			}

			delete reg->lista;
			delete reg;
			reg = NULL;
		}
	}

	return NULL;
}

int IteradorIndice::busquedaBinaria(string termino, int pos_inicial, int tam_pagina, int tam_bloque){
	if (tam_pagina == 1){
		return pos_inicial;
	}

	int bloque_medio = tam_pagina / 2;

	int pos = pos_inicial + bloque_medio * tam_bloque;
	this->setPosicion(pos);

	super_reg* reg = this->proximoRegistro();
	if (reg->termino == termino){
		return pos;
	}else if (reg->termino < termino){
		return this->busquedaBinaria(termino, pos, tam_pagina - bloque_medio, tam_bloque);
	}else if (reg->termino > termino){
		return this->busquedaBinaria(termino, pos_inicial, bloque_medio, tam_bloque);
	}
}

IteradorIndice::~IteradorIndice() {
	this->indice->close();
	delete this->indice;

	this->terminos->close();
	delete this->terminos;

	this->docs->cerrar();
	delete this->docs;

	delete this->header;
}

